#ifndef _CU_IMAGE_H_
#define _CU_IMAGE_H_

#include "../cuCommon/buffer.h"

#include "../devil/inc/il.h"
#include "../devil/inc/ilu.h"
#include "../devil/inc/ilut.h"

typedef TBuffer<uchar4> Trgba8;

class cuImage
{
	Trgba8 m_data;

	ILuint m_il;

	void Null()
	{
		m_il = 0;
	}

public:
	cuImage() { Null(); }
	~cuImage(){ m_data.Release(m_data.flag()); }
	cuImage(cuImage & i) { m_data = i.m_data; }

	cuImage & operator = (cuImage & i) { m_data = i.m_data; return *this; }

	ILboolean Load(char const * fileneame);
	ILboolean Save(char const * filename);

	void Create(uint8 createFlag) { m_data.Create(createFlag); }
	void Create(uint8 createFlag, uint32 w, uint32 h) { m_data.Create(createFlag, w, h); }

	void Release(uint8 releaseFlag) { m_data.Release(releaseFlag); }

	uint32 Width()  { return m_data.dim().x; }
	uint32 Height() { return m_data.dim().y; }
	uint32 Pitch()  { return m_data.whd().x; }

	//////////////////////////////////////////////////////////////////////////
	/// Run-time methods
	//////////////////////////////////////////////////////////////////////////

	//////////////////////////////////////////////////////////////////////////
	/// Pixel color transforms
	//////////////////////////////////////////////////////////////////////////
	friend void Gamma(cuImage & dst, cuImage & src, float g);
	friend void RGB_to_HSV(cuImage & dst, cuImage & src);
	friend void HSV_to_RGB(cuImage & dst, cuImage & src);
	friend void RGB_to_YUV(cuImage & dst, cuImage & src);
	friend void YUV_to_RGB(cuImage & dst, cuImage & src);
	friend void Negative(cuImage & dst, cuImage & src);
	friend void Luminance(cuImage & dst, cuImage & src);
	
	//////////////////////////////////////////////////////////////////////////
	/// Filtering
	//////////////////////////////////////////////////////////////////////////
	friend void BoxBlur(cuImage & dst, cuImage & src, float radius);
	friend void Convolution2D(cuImage & dst, cuImage & src, float * pKernel, int c1, int c2, int r1, int r2);
	friend void Convolution2D_smem(cuImage & dst, cuImage & src, float * pKernel, int c1, int c2, int r1, int r2);
	friend void ConvolutionX(cuImage & dst, cuImage & src, float * pKernel, int c1, int c2);
	friend void ConvolutionY(cuImage & dst, cuImage & src, float * pKernel, int r1, int r2);
	friend void Convolution2D_separable(cuImage & dst, cuImage & src, float * pKernelX, float * pKernelY, int c1, int c2, int r1, int r2);
	friend void GaussianX(cuImage & dst, cuImage & src, float sigma = 1.0f);
	friend void GaussianY(cuImage & dst, cuImage & src, float sigma = 1.0f);
	friend void Gaussian2D_separable(cuImage & dst, cuImage & src, float sigma = 1.0f);
	friend void Bilateral2D(cuImage & dst, cuImage & src, int radius, float noise, float weight_threshold = 0.0f);
	friend void BilateralX(cuImage & dst, cuImage & src, int radius, float noise, float weight_threshold = 0.0f);
	friend void BilateralY(cuImage & dst, cuImage & src, int radius, float noise, float weight_threshold = 0.0f);
	friend void Bilateral2D_separable(cuImage & dst, cuImage & src, int radius, float noise, float weight_threshold = 0.0f);
	friend void NonLocalMeans_tex(cuImage & dst, cuImage & src, int radius, int window, float noise, float weight_threshold = 0.0f);
	friend void NonLocalMeans_tex_smem(cuImage & dst, cuImage & src, int radius, int window, float noise, float weight_threshold = 0.0f);
	friend void NonLocalMeans_gmem_smem(cuImage & dst, cuImage & src, int radius, int window, float noise, float weight_threshold = 0.0f);

	//////////////////////////////////////////////////////////////////////////
	/// Resampling
	//////////////////////////////////////////////////////////////////////////
	friend void Bilinear (cuImage & dst, cuImage & src, cuImage & dst_y);
	friend void Bicubic (cuImage & dst, cuImage & src, cuImage & dst_y, float blur = 1.0f);
	friend void MonotoneBicubic(cuImage & dst, cuImage & src, cuImage & dst_y);
	friend void HermiteSpline(cuImage & dst, cuImage & src, cuImage & dst_y, float c = 0.5f);
  friend void Lanczos(cuImage & dst, cuImage & src, cuImage & dst_y, int radius = 3, float blur = 1.0f);
  friend void NEDI_12(cuImage & dst, cuImage & src, cuImage & dst_12);

	//////////////////////////////////////////////////////////////////////////
	/// Edge detection
	//////////////////////////////////////////////////////////////////////////
	friend void Roberts(cuImage & dst, cuImage & src);
	friend void Roberts_smem(cuImage & dst, cuImage & src);
	friend void Laplace(cuImage & dst, cuImage & src);
	friend void Prewitt(cuImage & dst, cuImage & src);
	friend void Sobel(cuImage & dst, cuImage & src);
	friend void Canny(cuImage & dst, cuImage & src, cuImage & blur, cuImage & magnitude, cuImage & direction, int radius, float noise, float weight_threshold = 0.0f);

	//////////////////////////////////////////////////////////////////////////
	/// Template methods
	//////////////////////////////////////////////////////////////////////////
	template <int radius> 
	friend void BoxBlur(cuImage & dst, cuImage & src);
	template <int radius, int window>
	friend void NonLocalMeans_tex(cuImage & dst, cuImage & src, float noise, float weight_threshold);
	template <int radius, int window> 
	friend void NonLocalMeans_tex_smem(cuImage & dst, cuImage & src, float noise, float weight_threshold);
	template <int radius, int window> 
	friend void NonLocalMeans_gmem_smem(cuImage & dst, cuImage & src, float noise, float weight_threshold);

	// Hardcore stuff
	template <int radius, int window, int bx, int by> 
	friend void NonLocalMeans_gmem_smem_32x32(cuImage & dst, cuImage & src, float noise, float weight_threshold);

	template <int radius>
	friend void Lanczos(cuImage & dst, cuImage & src, cuImage & dst_y, float blur);

	//////////////////////////////////////////////////////////////////////////
	/// Static methods
	//////////////////////////////////////////////////////////////////////////
	static void Init();

	//////////////////////////////////////////////////////////////////////////
	/// Copy & Set methods
	//////////////////////////////////////////////////////////////////////////
	void Memcpy(uint8 dirFlag) { m_data.Memcpy(dirFlag); }
	void Memset(uint8 bufFlag) { m_data.Memset(bufFlag, NULL); } 
};

#endif